Agenda

Announcements

  • MDSR Ch 10 programming notebook assigned

MDSR Ch 10 Errata / Tips

  • Some sections don’t require programming, but please still include the headers for navigation purposes
  • p. 234-235: Rename the result for 20,000 simulations as sim_results20k
    • the authors reuse the object name sim_results for the example with 20,000 simulations, but this overwrites the object expected for the plot at the top of p. 235.
    • by renaming the object resulting form 20,000 simulations, the ggplot call will still be able to access the intended object

Purposes for Simulation

Intro to Simulation

Monte Carlo simulation

Light at Night

# data intake
NightLightRaw <- read_csv("LightatNight.csv", col_names = TRUE)
Parsed with column specification:
cols(
  Light = col_character(),
  BodyMass0 = col_double(),
  BodyMass8 = col_double(),
  BMChange = col_double(),
  Corticosterone = col_double(),
  DayPct = col_double(),
  Consumption = col_double(),
  GlucoseInt = col_character(),
  GTT15 = col_double(),
  GTT120 = col_double(),
  Activity = col_double()
)
# subsetting for comparison of interest
NightLight <- 
  NightLightRaw %>%
  filter(Light %in% c("bright", "dark")) %>%
  select(Light, BMChange)
# summary statistics for each group
favstats(BMChange ~ Light, data = NightLight)
ObsMean <- mean(BMChange ~ Light, data = NightLight, na.rm = TRUE)
ObsMeanDiff <- ObsMean["bright"] - ObsMean["dark"]

Light at Night Simulation

# simulate with mosaic::do()
NightLightSims <- 
  mosaic::do(1000) * 
    NightLight %>%
    mutate(Light = shuffle(Light)) %>%
    group_by(Light) %>%
    summarise(meanBMChange = mean(BMChange, na.rm = TRUE)) 

# results after `shuffle( )`
favstats(meanBMChange ~ Light, data = NightLightSims)

# mean differences for shuffled data
LightSimResults <- 
  NightLightSims %>%
  select(-.row) %>%
  spread(key = Light, value = meanBMChange) %>%
  mutate(meanDiff = bright - dark) 


# distribution of outcomes expected by chance (random simulations)
p <- 
  LightSimResults %>%
  ggplot(aes(x = meanDiff)) + 
  geom_density() 
p

# comparison with observed outcome
p + geom_vline(xintercept = ObsMeanDiff)

Back to NCI60

NCI60 data reduction

NCI60 data reduction

NCI60 <- read_csv("NCI60.csv") 
Parsed with column specification:
cols(
  .default = col_double(),
  Probe = col_character()
)
See spec(...) for full column specifications.
head(NCI60)
Spreads <- 
  NCI60 %>%
  gather(value = expression, key = cellLine, -Probe) %>%
  group_by(Probe) %>%
  summarise(N = n(), 
            spread = sd(expression)) %>%
  arrange(desc(spread)) %>%
  mutate(order = row_number())

Simulations for NCI60 data reduction

Sim_spreads <- 
  NCI60 %>%
  gather(value = expression, key = cellLine, -Probe) %>%
  mutate(Probe = shuffle(Probe)) %>%
  group_by(Probe) %>%
  summarise(N = n(), 
            spread = sd(expression)) %>%
  mutate(order = row_number())
threshold <- max(Sim_spreads$spread)
Spreads %>%
  filter(order <= 500) %>%
  ggplot(aes(x = order, y = spread)) + 
  ylim(limits = c(2.5, 7.5)) + 
  geom_line(size = 1) + 
  geom_hline(yintercept = threshold, color = "red", size = 1, linetype = 2) + 
  annotate("text", x = 400, y = threshold + 0.2, label = "Largest random outcome")

# head(Spreads, 10)

Randomness is the key to simulation

image credit: https://m.xkcd.com/1210/

image credit: https://m.xkcd.com/1210/

Random number generators

note about uniform[0, 1]

Brush with destiny

Simulation

n <- 10000
sim_meet <- data.frame(
  you <- runif(n, min = 0, max = 60), 
  destiny <- runif(n, min = 0, max = 60)) %>%
  mutate(result = ifelse(abs(you - destiny) <= 10, 
                         "Same time, same place!", "So close, and yet so far..."))
tally(~ result, format = "percent", data = sim_meet)
result
     Same time, same place! So close, and yet so far... 
                      30.37                       69.63 
destinyModel <- binom.test(~ result, n, success = "Same time, same place!", data = sim_meet)
confint(destinyModel)
sim_meet %>%
  ggplot() + 
  geom_point(aes(x = destiny, y = you, color = result), alpha = 0.3) + 
  ylab("You arrive (minutes after 10am)") + 
  xlab("Destiny arrives (minutes after 10am)") + 
  ggtitle("(simulated) Probability you'll meet your destiny at Starbucks")

NA

Key principles in simulation

Key principles: Design

Brush with destiny design

  • Starbucks arrival time is uniform between 10am & 11am (60 minute window) for both
  • if you’re both there at the same time, you’ll definitely meet
  • Q: how might you challenge these assumptions?

Key principles: Modularity

starbucks_sim <- function(num_sim = 1000, wait = 10) { 
  # purpose: simulate chance meeting between two people 
  ### num_sim: number of times to replicate simulation
  ### wait: number of minutes between arrivals for successful meeting
sim_meet <- data.frame(
  you <- runif(n, min = 0, max = 60), 
  destiny <- runif(n, min = 0, max = 60)) %>%
  mutate(result = ifelse(abs(you - destiny) <= 10, 
                         "Same time, same place!", "So close, and yet so far..."))
destinyModel <- binom.test(~ result, n, success = "Same time, same place!", data = sim_meet)
return(confint(destinyModel))
}
starbucks_sim()

Key principles: Reproducibility

starbucks_sim()
# choose seed
set.seed(23)  # set initial state of RNG seed
runif(n = 5)  # new outcome
[1] 0.5766 0.2231 0.3319 0.7107 0.8194
runif(n = 5)  # new outcome
[1] 0.4237 0.9635 0.9781 0.8405 0.9966
# this is why we say *psuedo* random
set.seed(23)  # set initial state of RNG seed
runif(n = 5)  # back to outcome 1 again
[1] 0.5766 0.2231 0.3319 0.7107 0.8194
# different seed
set.seed(301) # new seed
runif(n = 5)  # new outcome
[1] 0.597106 0.132231 0.002137 0.753970 0.614147

Key principles: Replication

simple_sim <- function(num_sim = 1000, wait = 10) { 
  # purpose: lean version of `starbucks_sim` to explore replication  
  you <- runif(num_sim, min = 0, max = 60)
  destiny <- runif(num_sim, min = 0, max = 60)
  return(sum(abs(you - destiny) <= wait) / num_sim)
}
reps <- 2500
params <- data.frame(num_sims = c(100, 400, 1600))
sim_results <- 
  params %>%
  group_by(num_sims) %>%
  dplyr::do(mosaic::do(reps) * simple_sim(num_sim = .$num_sims, wait = 10))

|=========================================================                             | 67% ~1 s remaining     
|======================================================================================|100% ~0 s remaining     
favstats(simple_sim ~ num_sims, data = sim_results)
sim_results %>%
  ggplot(aes(x = simple_sim, color = factor(num_sims))) + 
  geom_density(size = 2) + 
  scale_x_continuous("Proportion of times you meet")

Modifying the simulation of our Brush with Destiny

Modifying the simulation of our Brush with Destiny

Assumptions modified

  1. your arrival time is 5 minutes + random delays
    • your minimum possible time is 5 minutes
    • random delays: rexp(n, .1)
      • typically 10 minutes or less
      • possibly longer
      • never negative
  2. other person arrival time is unknown + random delays
    • fastest arrival is unknown, we assume uniform between 3 & 20 minutes
    • random delays: rexp(n, .1)
  3. maybe 5% chance you’re both willing to strike up a meaningful conversation with a stranger at Starbucks on any given day
  4. Q: Share some additional things we still haven’t considered here!
  • Note about modeling waiting time (i.e., delay)
    • Lots of “name-brand” probability distributions model waiting time scenarios
    • don’t get stuck on the details of that choice here, just pay attention in STAT 414!
    • our choice: ggplot() + geom_density(aes(x = rexp(n, .1)))
n <- 100000
sim_meetExp <- data.frame(
  you <- 5 + rexp(n, 0.1),                      # your arrival time (5 min + random delay)
  destiny <- runif(n, min = 3, max = 20) +      # fastest arrival is unknown between 3 & 20 min
             rexp(n, 0.1)) %>%                  # random delay
  mutate(opportunity = ifelse(abs(you - destiny) <= 10, 
                         "Same time, same place!", "So close, and yet so far..."), 
         talkativeMood = sample(c(TRUE, FALSE), size = n,    
                                replace = TRUE, prob = c(0.05, 0.95)),
         result = ifelse(test = (opportunity == "Same time, same place!" & talkativeMood == TRUE), 
                         yes = "Bliss", no = "Oh well..."))
tally(~ result, format = "percent", data = sim_meetExp)
result
     Bliss Oh well... 
     2.512     97.488 
destinyModel <- binom.test(~ result, n, success = "Bliss", data = sim_meetExp)
confint(destinyModel)
sim_meetExp %>%
  ggplot() + 
  geom_point(aes(x = destiny, y = you, color = result), alpha = 0.3) + 
  ylab("You arrive (minutes after 10am)") + 
  xlab("Destiny arrives (minutes after 10am)") + 
  ggtitle("This is why it's so hard to meet people...") + 
  scale_x_continuous(limits = c(0, 60)) + 
  scale_y_continuous(limits = c(0, 60)) 

NA

Simulating Models

Simulating simple linear regression data

Consider a simple linear regression model:

\[y = \beta_0 + \beta_1*x\]

Simulating simple linear regression data

Consider a logistic regression model:

\[E[Y | X] = \beta_0 + \beta_1*X\]

\[y_i = b_0 + b_1*x_i + \epsilon_i\]

# beta_0 
intercept <- 26
# beta_1
beta <- -2.5
# sample size for our simulated model
n <- 5000
# X is a simulated random variable representing some measurable characteristic of the cases 
xtest <- rnorm(n, mean = 10, sd = 1)
# simulate errors: assume i.i.d. N(0, sigma)
errors <- rnorm(n, mean = 0, sd = 5)
# Y is a simulated random variable representing our response
ytest <- intercept + (xtest * beta) + errors
# fit the simulated logistic regression model
simreg <- lm(ytest ~ xtest)
# how did we do?
msummary(simreg)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  25.0274     0.7180    34.9   <2e-16 ***
xtest        -2.4117     0.0712   -33.9   <2e-16 ***

Residual standard error: 5.01 on 4998 degrees of freedom
Multiple R-squared:  0.187, Adjusted R-squared:  0.186 
F-statistic: 1.15e+03 on 1 and 4998 DF,  p-value: <2e-16
# confint(simreg)

Simulating logistic regression data

Consider a logistic regression model:

\[log(\frac{\pi}{1-\pi}) = \beta_0 + \beta_1*x\]

Simulating logistic regression data

Consider a logistic regression model:

\[log(\frac{\pi}{1-\pi}) = \beta_0 + \beta_1*x\]

\[log(\frac{p_i}{1-p_i}) = b_0 + b_1*x_i\]

# beta_0
intercept <- -1
# beta_1
beta <- 0.5
# sample size for our simulated model
n <- 5000
# X is a simulated random variable representing some measurable characteristic of the cases 
xtest <- rnorm(n, mean = 1, sd = 1)
# recall: the log-odds is a *linear* model with respect to our explanatory/predictor variables
linpred <- intercept + (xtest * beta)
# transform predictions to probabilities
prob <- exp(linpred)/(1 + exp(linpred))
# Y is a simulated random variable representing our response; the "errors" are built-in here
ytest <- ifelse(runif(n) < prob, 1, 0)
# fit the simulated logistic regression model
logreg <- glm(ytest ~ xtest, family=binomial)
# how did we do?
coef(logreg)
(Intercept)       xtest 
    -1.0136      0.5062 
confint(logreg)
Waiting for profiling to be done...
              2.5 %  97.5 %
(Intercept) -1.1048 -0.9238
xtest        0.4444  0.5687
## interpret odds
exp(coef(logreg))
(Intercept)       xtest 
     0.3629      1.6590 
exp(confint(logreg))
Waiting for profiling to be done...
             2.5 % 97.5 %
(Intercept) 0.3313  0.397
xtest       1.5596  1.766

Evaluating Model Assumptions

Evaluating Simple Linear Regression Assumptions

Challenging the equal variance assumption

\[E[Y|X] = \beta_0 + \beta_1X_1 + \beta_2X_2\]

\[y_i = b_0 + b_1x_{1i} + b_2x_{2i} + \epsilon_i\]

# sample size for our simulation
n <- 250
# sd of model errors
rmse <- 1
# two explanatory variables
x1 <- runif(n, min = 0, max = 15)
# model coefficients
beta0 <- -3
beta1 <- 0.5
# simulate response data
y <- beta0 + beta1*x1 + rnorm(n, mean = 0, sd = rmse)        # equal variance
# y <- beta0 + beta1*x1 + rnorm(n, mean = 0, sd = rmse + x1)   # UNequal variance
# plot data with smoother
data.frame(y, x1) %>%
  ggplot(aes(x = x1, y = y)) + 
  geom_point() +
  geom_smooth()

# regression model
simLM <- lm(y ~ x1)
# check the model fit
msummary(simLM)
            Estimate Std. Error t value Pr(>|t|)    
(Intercept)  -3.0248     0.1320   -22.9   <2e-16 ***
x1            0.4986     0.0149    33.4   <2e-16 ***

Residual standard error: 0.983 on 248 degrees of freedom
Multiple R-squared:  0.818, Adjusted R-squared:  0.817 
F-statistic: 1.11e+03 on 1 and 248 DF,  p-value: <2e-16
mplot(simLM)[1]  # residual diagnostics
[[1]]

Simulate many models

dosim <- function() {
  y <- beta0 + beta1*x1 + rnorm(n, mean = 0, sd = rmse + x1)   # departure
  mod <- lm(y ~ x1)
  result <- coefficients(mod)
  return(result)
}
sims <- mosaic::do(1000) * dosim()
favstats(~ x1, data = sims)
# distribution coefficient estimates
sims %>%
  ggplot(aes(x = x1)) +
  geom_density() +
  # pass arguments to `dnorm` function
  stat_function(fun = dnorm, args = list(mean = mean(sims$x1), sd = sd(sims$x1)),
                linetype = 2) +
  ggtitle("distribution of regression parameter") +
  scale_x_continuous("beta 1 coefficients")

Simulating a complex system

any_active <- function(df) {
  # return TRUE if someone has not finished
  return(max(df$endtime) == Inf)
}
next_customer <- function(df) {
  # returns the next customer in line
  res <- filter(df, endtime == Inf) %>%
    arrange(arrival)
  return(head(res, 1))
}
update_customer <- function(df, cust_num, end_time) {
  # sets the end time of a specific customer
  return(mutate(df, endtime = ifelse(custnum == cust_num, end_time, endtime)))
}
  
run_sim <- function(n = 1/2, m = 3/2, hours = 6) {
  # simulation of bank where there is just one teller
  # n: expected number of customers per minute
  # m: expected length of transaction is m minutes
  # hours: bank open for this many hours
  
  customers <- rpois(hours * 60, lambda = n)
  arrival <- numeric(sum(customers))
  position <- 1
  for (i in 1:length(customers)) {
    numcust <- customers[i]
    if (numcust != 0) {
      arrival[position:(position + numcust - 1)] <- rep(i, numcust)
      position <- position + numcust
    }
  }
  duration <- rexp(length(arrival), rate = 1/m)  # E[X]=m
  df <- data.frame(arrival, duration, custnum = 1:length(duration), 
                   endtime = Inf, stringsAsFactors = FALSE)
  
  endtime <- 0 # set up beginning of simulation
  while (any_active(df)) { # anyone left to serve
    next_one <- next_customer(df)
    now <- ifelse(next_one$arrival >= endtime, next_one$arrival, endtime)
    endtime <- now + next_one$duration
    df <- update_customer(df, next_one$custnum, endtime)
  }
  df <- mutate(df, totaltime = endtime - arrival)
  return(favstats(~ totaltime, data = df))
}
sim_results <- mosaic::do(3) * run_sim()
sim_results
LS0tCnRpdGxlOiAiU2ltdWxhdGlvbiIKc3VidGl0bGU6ICJNRFNSIENoIDEwIgpvdXRwdXQ6IAogIHNsaWR5X3ByZXNlbnRhdGlvbjogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQgIAotLS0KCgpgYGB7ciBGcm9udCBNYXR0ZXIsIGVjaG89VFJVRSwgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRX0KIyBjbGVhbiB1cCBSIGVudmlyb25tZW50CnJtKGxpc3QgPSBscygpKQoKIyBnbG9iYWwgb3B0aW9ucwprbml0cjo6b3B0c19jaHVuayRzZXQoZXZhbD1UUlVFLCBpbmNsdWRlPVRSVUUpCm9wdGlvbnMoZGlnaXRzPTQpCgojIHBhY2thZ2VzIHVzZWQKbGlicmFyeShtZHNyKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCiMgaW5wdXRzIHN1bW1hcnkKCgpgYGAKCgojIyBBZ2VuZGEKCgojIyMjIEFubm91bmNlbWVudHMKCi0gTURTUiBDaCAxMCBwcm9ncmFtbWluZyBub3RlYm9vayBhc3NpZ25lZAoKIyMjIyBNRFNSIENoIDEwIEVycmF0YSAvIFRpcHMKCi0gU29tZSBzZWN0aW9ucyBkb24ndCByZXF1aXJlIHByb2dyYW1taW5nLCBidXQgcGxlYXNlIHN0aWxsIGluY2x1ZGUgdGhlIGhlYWRlcnMgZm9yIG5hdmlnYXRpb24gcHVycG9zZXMKLSBwLiAyMzQtMjM1OiBSZW5hbWUgdGhlIHJlc3VsdCBmb3IgMjAsMDAwIHNpbXVsYXRpb25zIGFzIGBzaW1fcmVzdWx0czIwa2AKICAgIC0gdGhlIGF1dGhvcnMgcmV1c2UgdGhlIG9iamVjdCBuYW1lIGBzaW1fcmVzdWx0c2AgZm9yIHRoZSBleGFtcGxlIHdpdGggMjAsMDAwIHNpbXVsYXRpb25zLCBidXQgdGhpcyBvdmVyd3JpdGVzIHRoZSBvYmplY3QgZXhwZWN0ZWQgZm9yIHRoZSBwbG90IGF0IHRoZSB0b3Agb2YgcC4gMjM1LiAgCiAgICAtIGJ5IHJlbmFtaW5nIHRoZSBvYmplY3QgcmVzdWx0aW5nIGZvcm0gMjAsMDAwIHNpbXVsYXRpb25zLCB0aGUgZ2dwbG90IGNhbGwgd2lsbCBzdGlsbCBiZSBhYmxlIHRvIGFjY2VzcyB0aGUgaW50ZW5kZWQgb2JqZWN0CgoKIyMgUHVycG9zZXMgZm9yIFNpbXVsYXRpb24KCi0gUTogV2hhdCBjb21lcyB0byBtaW5kIHdoZW4geW91IHRoaW5rIG9mICJzaW11bGF0aW9uIgotIFE6IFdoYXQgYXJlIHRoZSBwdXJwb3NlcyBmb3Igc2ltdWxhdGlvbj8KCgojIyBJbnRybyB0byBTaW11bGF0aW9uCgotIFNpbXVsYXRpb25zIGFyZSBvZnRlbiB1c2VmdWwgdG8gbGVhcm4gYWJvdXQgdGhlIG5hdHVyZSBvZiBhIHJhbmRvbSBwcm9jZXNzCi0gV2UgYnVpbGQgYSByZWFsaXN0aWMgIm1vZGVsIiBvZiB0aGUgc3lzdGVtL3Byb2Nlc3MgdGhhdCB3ZSB3YW50IHRvIHN0dWR5CiAgICAtIFRoZSBhY3Qgb2YgYnVpbGRpbmcgaXRzZWxmIG9mdGVuIGxlYWRzIHRvIGRlZXBlciB1bmRlcnN0YW5kaW5nCiAgICAtIFdlIHN0dWR5IG91dGNvbWVzIHByb2R1Y2VkIGJ5IG91ciBtb2RlbCB0byBidWlsZCBpbnR1aXRpb24gYWJvdXQgdGhlIG5hdHVyZSBvZiB0aGUgc3lzdGVtIGFuZCBpdCdzIG91dGNvbWVzCi0gKipXaW5ub3dpbmcgb3V0IGh5cG90aGVzZXMqKjogCiAgICAtIHdlIG9mdGVuIGhhdmUgbWFueSBjb21wZXRpbmcgaHlwb3RoZXNlcyBhYm91dCBob3cgYSByZWFsIHN5c3RlbSB3b3JrcwogICAgLSB3ZSBjYW4gc2ltdWxhdGUvZ2VuZXJhdGUgZGF0YSBmcm9tIGVhY2ggaHlwb3RoZXNpcyBhbmQgcnVsZSBvdXQgdGhlIG9uZXMgdGhhdCBhcmVuJ3QgY29uc2lzdGVudCB3aXRoIHRoZSBvYnNlcnZlZCBkYXRhCiAgICAtIGUuZy4sIG91ciByYW5kb21pemF0aW9uIHRlc3QKLSAqKkNvbmRpdGlvbmFsIGluZmVyZW5jZSoqOiAKICAgIC0gYnVpbGQgYSBzeXN0ZW0gdGhhdCByZWZsZWN0cyBob3cgYSByZWFsIHN5c3RlbSB3b3JrcwogICAgLSB1c2UgdGhlIGRhdGEgdGhhdCBpdCBwcm9kdWNlcyB0byBidWlsZCBpbnR1aXRpb24gYWJvdXQgdGhlIHJlYWwgd29ybGQKCiMjIE1vbnRlIENhcmxvIHNpbXVsYXRpb24KCi0gWW91J2xsIGhlYXIgdGhlIHRlcm0gIk1vbnRlIENhcmxvIiB0aHJvd24gYXJvdW5kIHdoZW4gcGVvcGxlIHRhbGsgYWJvdXQgc2ltdWxhdGlvbgotIE1vbnRlIENhcmxvIHNpbXVsYXRpb24gdGVtcGxhdGUgCiAgICAtIGRlZmluZSBzb21lIGRvbWFpbiBvZiBwb3NzaWJsZSBpbnB1dHMKICAgIC0gdXNlIGEgcHJvYmFiaWxpdHkgZGlzdHJpYnV0aW9uIHRvIGdlbmVyYXRlIGlucHV0cyBvdmVyIHRoZSBkb21haW4KICAgIC0gcGVyZm9ybSBzb21lIGRldGVybWluaXN0aWMgY29tcHV0YXRpb24gb24gdGhlIGlucHV0cwogICAgLSBhZ2dyZWdhdGUgdGhlIHJlc3VsdHMKLSBNb250ZSBDYXJsbyBzaW11bGF0aW9ucyByZWx5IG9uIHRoZSAqKkxhdyBvZiBMYXJnZSBOdW1iZXJzKiogCiAgICAtIHRoZSBhdmVyYWdlIG9mIGEgbGFyZ2Ugc2FtcGxlIG91Z2h0IHRvIGJlIGNsb3NlIHRvIHRoZSBleHBlY3RlZCB2YWx1ZSAKICAgIC0gdGhlIGxhcmdlciB0aGUgc2FtcGxlLCB0aGUgY2xvc2VyIGl0IHRlbmRzIHRvIGJlCgoKIyMgTGlnaHQgYXQgTmlnaHQKCiFbXShMaWdodGF0TmlnaHQucG5nKQoKLSBSZXNlYXJjaGVycyBpbnRlcmVzdGVkIGluIHRoZSAibGluayBiZXR3ZWVuIHRoZSBtb2xlY3VsYXIgY2lyY2FkaWFuIGNsb2NrICYgbWV0YWJvbGlzbSIKLSBUaGUgc3R1ZHkgZGF0YSBzaG93cyB0aGF0IGJvZHkgbWFzcyBvZiBtaWNlIGluICJicmlnaHQiIGdyb3VwIGluY3JlYXNlZCBieSBhYm91dCAzIGdyYW1zIG1vcmUsIG9uIGF2ZXJhZ2UsIHRoYW4gbWljZSBpbiAiZGFyayIgZ3JvdXAKICAgIC0gUTogaXMgdGhpcyBhIG1lYW5pbmdmdWwgY2hhbmdlLCBvciBqdXN0IHJhbmRvbW5lc3M/CiAgICAtIFE6IElmIGl0IHdlcmUgYWxsIGp1c3QgcmFuZG9tbmVzcywgaG93IGNvdWxkIHdlIHNpbXVsYXRlIG91dGNvbWVzIHRoYXQgbWlnaHQgYmUgb2JzZXJ2ZWQgYnkgY2hhbmNlIGFsb25lPwogICAgICAgIDEuICoqV2l0aG91dCoqIGEgY29tcHV0ZXI/CiAgICAgICAgMi4gV2l0aCBhIGNvbXB1dGVyPwoKYGBge3J9CiMgZGF0YSBpbnRha2UKTmlnaHRMaWdodFJhdyA8LSByZWFkX2NzdigiTGlnaHRhdE5pZ2h0LmNzdiIsIGNvbF9uYW1lcyA9IFRSVUUpCgojIHN1YnNldHRpbmcgZm9yIGNvbXBhcmlzb24gb2YgaW50ZXJlc3QKTmlnaHRMaWdodCA8LSAKICBOaWdodExpZ2h0UmF3ICU+JQogIGZpbHRlcihMaWdodCAlaW4lIGMoImJyaWdodCIsICJkYXJrIikpICU+JQogIHNlbGVjdChMaWdodCwgQk1DaGFuZ2UpCgojIHN1bW1hcnkgc3RhdGlzdGljcyBmb3IgZWFjaCBncm91cApmYXZzdGF0cyhCTUNoYW5nZSB+IExpZ2h0LCBkYXRhID0gTmlnaHRMaWdodCkKCk9ic01lYW4gPC0gbWVhbihCTUNoYW5nZSB+IExpZ2h0LCBkYXRhID0gTmlnaHRMaWdodCwgbmEucm0gPSBUUlVFKQpPYnNNZWFuRGlmZiA8LSBPYnNNZWFuWyJicmlnaHQiXSAtIE9ic01lYW5bImRhcmsiXQpgYGAKCiMjIExpZ2h0IGF0IE5pZ2h0IFNpbXVsYXRpb24KCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9VFJVRX0KIyBzaW11bGF0ZSB3aXRoIG1vc2FpYzo6ZG8oKQpOaWdodExpZ2h0U2ltcyA8LSAKICBtb3NhaWM6OmRvKDEwMDApICogCiAgICBOaWdodExpZ2h0ICU+JQogICAgbXV0YXRlKExpZ2h0ID0gc2h1ZmZsZShMaWdodCkpICU+JQogICAgZ3JvdXBfYnkoTGlnaHQpICU+JQogICAgc3VtbWFyaXNlKG1lYW5CTUNoYW5nZSA9IG1lYW4oQk1DaGFuZ2UsIG5hLnJtID0gVFJVRSkpIAoKIyByZXN1bHRzIGFmdGVyIGBzaHVmZmxlKCApYApmYXZzdGF0cyhtZWFuQk1DaGFuZ2UgfiBMaWdodCwgZGF0YSA9IE5pZ2h0TGlnaHRTaW1zKQoKIyBtZWFuIGRpZmZlcmVuY2VzIGZvciBzaHVmZmxlZCBkYXRhCkxpZ2h0U2ltUmVzdWx0cyA8LSAKICBOaWdodExpZ2h0U2ltcyAlPiUKICBzZWxlY3QoLS5yb3cpICU+JQogIHNwcmVhZChrZXkgPSBMaWdodCwgdmFsdWUgPSBtZWFuQk1DaGFuZ2UpICU+JQogIG11dGF0ZShtZWFuRGlmZiA9IGJyaWdodCAtIGRhcmspIAoKCiMgZGlzdHJpYnV0aW9uIG9mIG91dGNvbWVzIGV4cGVjdGVkIGJ5IGNoYW5jZSAocmFuZG9tIHNpbXVsYXRpb25zKQpwIDwtIAogIExpZ2h0U2ltUmVzdWx0cyAlPiUKICBnZ3Bsb3QoYWVzKHggPSBtZWFuRGlmZikpICsgCiAgZ2VvbV9kZW5zaXR5KCkgCnAKCiMgY29tcGFyaXNvbiB3aXRoIG9ic2VydmVkIG91dGNvbWUKcCArIGdlb21fdmxpbmUoeGludGVyY2VwdCA9IE9ic01lYW5EaWZmKQogIApgYGAKCgoKIyMgQmFjayB0byBOQ0k2MAoKLSBDYW5jZXIgdHlwZXMgYXJlIG9mdGVuIG5hbWVkIGZvciB0aGUgbG9jYXRpb24gd2hlcmUgdGhleSdyZSBmb3VuZAogICAgLSBsdW5nLCAKICAgIC0gb3ZhcmlhbiwgCiAgICAtIGJyZWFzdCwgCiAgICAtIGV0YwotICoqUHJvYmxlbTogdGlzc3VlIG9mIG9yaWdpbiBtYXkgbm90IGJlIHRoZSBiZXN0IGluZGljYXRvciBmb3IgYXBwcm9wcmlhdGUgdHJlYXRtZW50KioKLSBQcmV2aW91c2x5LCB3ZSB1c2VkIHVuc3VwZXJ2aXNlZCBsZWFybmluZyBtZXRob2RzIHRvIGV4cGxvcmUgcmVsYXRpb25zaGlwcyBhbW9uZyB2YXJpb3VzIGNhbmNlciBjZWxsIGxpbmVzIAogICAgLSBHb29kIGlkZWEsIGJ1dCBpdCdzIGEgY2hhbGxlbmdpbmcgcHJvYmxlbQogICAgLSBJdCdzIGhhcmQgdG8gc2F5IGlmIHdlIGZvdW5kIG11Y2ggb2YgdmFsdWUKLSBhbGwgY2VsbHMgaGF2ZSBhIGdlbm9tZSB3aXRoIGxvdHMgb2YgZGlmZmVyZW50IGdlbmVzIHRoYXQgdGhlIGJlaGF2aW9yCiAgICAtIG1pY3JvYXJyYXlzIGFyZSB1c2VkIHRvIGV4YW1pbmUgdGhlIGV4cHJlc3Npb24gb2YgaW5kaXZpZHVhbCBnZW5lcyB3aXRoaW4gYSBjZWxsCiAgICAgICAgLSBlYWNoIG1pY3JvYXJyYXkgaGFzIG1hbnkgKGRvemVuczsgaHVuZHJlZHM7IHRob3VzYW5kcykgcHJvYmVzIHRoYXQgcHJvdmlkZSBhIHNuYXBzaG90IG9mIGdlbmUgYWN0aXZpdHkKICAgICAgICAtIGNvbXBhcmlzb25zIGFtb25nIGNlbGxzIGluIGRpZmZlcmVudCBzdGF0ZXMgY2FuIHJldmVhbCBjbHVlcyBhYm91dCB3aGljaCBnZW5lcyBhcmUgbGlua2VkIHRvIGRpZmZlcmVudCBhc3BlY3RzIG9mIGNlbGwgYWN0aXZpdHkKLSBQcm9ibGVtOgogICAgLSBtb3N0IGdlbmVzIHJlZ3VsYXRlICoqbXVuZGFuZSoqIGJlaGF2aW9ycyB0eXBpY2FsIG9mIGFsbCBjZWxscy4uLiB3ZSBkb24ndCBjYXJlIGFib3V0IHRoZXNlCiAgICAtIHNvbWUgZ2VuZXMgcmVndWxhdGUgKipwcm9ibGVtKiogYmVoYXZpb3JzIHJlbGF0ZWQgdG8gY2FuY2VyIGNlbGxzIChlLmcuLCBvdmVyLXJhcGlkIHJlcHJvZHVjdGlvbikKICAgIC0gKip3ZSB3YW50IHRvIHNlcGFyYXRlIHRoZSB2aXRhbCBmZXcgZ2VuZXMgZnJvbSB0aGUgdHJpdmlhbCBtYW55Li4uKioKICAgIC0gUTogQW55IGdvb2QgaWRlYXM/CgojIyBOQ0k2MCBkYXRhIHJlZHVjdGlvbgoKLSBHb2FsOiB3ZSB3YW50IHRvIHNlcGFyYXRlIHRoZSB2aXRhbCBmZXcgZ2VuZXMgZnJvbSB0aGUgdHJpdmlhbCBtYW55IAogICAgLSBtb3N0IGdlbmVzIHJlZ3VsYXRlICoqbXVuZGFuZSoqIGJlaGF2aW9ycyB0eXBpY2FsIG9mIGFsbCBjZWxscy4uLiB3ZSBkb24ndCBjYXJlIGFib3V0IHRoZXNlCiAgICAtIHNvbWUgZ2VuZXMgcmVndWxhdGUgKipwcm9ibGVtKiogYmVoYXZpb3JzIHJlbGF0ZWQgdG8gY2FuY2VyIGNlbGxzIChlLmcuLCBvdmVyLXJhcGlkIHJlcHJvZHVjdGlvbikKLSBTaW1wbGUgSWRlYTogCiAgICAtIGlmIGV4cHJlc3Npb24gb2YgYSBwcm9iZSBpcyB0aGUgc2FtZSBhY3Jvc3MgYWxsIGF2YWlsYWJsZSBjYW5jZXIgc2FtcGxlcywgaXQgaXNuJ3QgdG8gZGlmZmVyZW50aWF0ZSB0aGVtIQogICAgLSBROiBIb3cgY2FuIHdlIHRlbGwgaWYgYSBwcm9iZSBpcyB0aGUgc2FtZSBhY3Jvc3MgYWxsIGF2YWlsYWJsZSBjYW5jZXIgc2FtcGxlcz8KICAgIC0gUTogSG93IHdpbGwgd2Ugc2VwYXJhdGUgdGhlIHZpdGFsIGZldyBmcm9tIHRoZSB0cml2aWFsIG1hbnk/IAoKIyMgTkNJNjAgZGF0YSByZWR1Y3Rpb24KCi0gZm9yIG5vdywgd2Ugc3RhcnQgd2l0aCB0cmFuc3Bvc2VkIE5DSTYwIGRhdGEgCiAgICAtIHByb2JlcyBhcmUgdGhlIGNhc2VzCiAgICAtIGNlbGwgbGluZXMgYXJlIHRoZSB2YXJpYWJsZXMKICAgIC0gdGhpcyBpcyB0aGUgb3Bwb3NpdGUgb2YgdGhlIGZvcm0gd2UgdXNlZCBmb3Igb3VyIGNsdXN0ZXIgYW5hbHlzaXMKLSBHb2FsOiB3ZSB3YW50IHRvIGNhbGN1bGF0ZSB0aGUgU0QgZm9yIGVhY2ggcHJvYmUgYW1vbmcgYWxsIGNlbGwgbGluZXMKICAgIC0gSGlnaCB2YXJpYWJpbGl0eSBhY3Jvc3MgY2VsbCBsaW5lcyBzdWdnZXN0cyB0aGUgcHJvYmUgbWlnaHQgYmUgdXNlZnVsIHRvIGRpZmZlcmVudGlhdGUgYW1vbmcgY2FuY2VyIHR5cGVzCiAgICAtIFE6IEhvdyB3aWxsIHdlIGtub3cgaG93IGhpZ2ggaXMgaGlnaCBlbm91Z2g/CiAgICAtIFE6IEhvdyBjb3VsZCB5b3UgKGh5cG90aGV0aWNhbGx5KSBzb2x2ZSB0aGlzIHdpdGhvdXQgYSBjb21wdXRlcj8KCgpgYGB7cn0KTkNJNjAgPC0gcmVhZF9jc3YoIk5DSTYwLmNzdiIpIApoZWFkKE5DSTYwKQoKU3ByZWFkcyA8LSAKICBOQ0k2MCAlPiUKICBnYXRoZXIodmFsdWUgPSBleHByZXNzaW9uLCBrZXkgPSBjZWxsTGluZSwgLVByb2JlKSAlPiUKICBncm91cF9ieShQcm9iZSkgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCksIAogICAgICAgICAgICBzcHJlYWQgPSBzZChleHByZXNzaW9uKSkgJT4lCiAgYXJyYW5nZShkZXNjKHNwcmVhZCkpICU+JQogIG11dGF0ZShvcmRlciA9IHJvd19udW1iZXIoKSkKYGBgCgo8IS0tIERheTIgLS0+CgojIyBTaW11bGF0aW9ucyBmb3IgTkNJNjAgZGF0YSByZWR1Y3Rpb24KCi0gV2Ugd2FudCB0byBzaW11bGF0ZSBhIG51bGwgZGlzdHJpYnV0aW9uIHN1Y2ggdGhhdDoKICAgIC0gd2UgYnJlYWsgYW55IGFzc29jaWF0aW9uIGJldHdlZW4gcHJvYmUgbGFiZWxzIGFuZCBnZW5lIGV4cHJlc3Npb24gbWVhc3VyZW1lbnRzCiAgICAtIEFsbCB0aGF0IHJlbWFpbnMgaXMgcmFuZG9tIHZhcmlhYmlsaXR5CiAgICAtIHdlIHRoZW4gY29tcGFyZSBvYnNlcnZlZCByZXN1bHRzIHdpdGggdGhlIHJhbmRvbSB2YXJpYWJpbGl0eQotIFE6IEhvdyBkb2VzIHRoZSBjb2RlIHNob3duIGFjY29tcGxpc2ggdGhpcyB0YXNrPwotIFE6IFdoYXQgY29uY2x1c2lvbnMgY2FuIHlvdSBkcmF3IGFzIGEgcmVzdWx0PwotIFE6IERlc2NyaWJlIHNpbWlsYXJpdGllcyAmIGRpZmZlcmVuY2VzIGJldHdlZW4gdGhpcyBleGFtcGxlICYgdGhlIHByb2Nlc3MgdXNlZCBpbiAiTGlnaHQgYXQgTmlnaHQiPwoKYGBge3J9ClNpbV9zcHJlYWRzIDwtIAogIE5DSTYwICU+JQogIGdhdGhlcih2YWx1ZSA9IGV4cHJlc3Npb24sIGtleSA9IGNlbGxMaW5lLCAtUHJvYmUpICU+JQogIG11dGF0ZShQcm9iZSA9IHNodWZmbGUoUHJvYmUpKSAlPiUKICBncm91cF9ieShQcm9iZSkgJT4lCiAgc3VtbWFyaXNlKE4gPSBuKCksIAogICAgICAgICAgICBzcHJlYWQgPSBzZChleHByZXNzaW9uKSkgJT4lCiAgbXV0YXRlKG9yZGVyID0gcm93X251bWJlcigpKQoKdGhyZXNob2xkIDwtIG1heChTaW1fc3ByZWFkcyRzcHJlYWQpCgpTcHJlYWRzICU+JQogIGZpbHRlcihvcmRlciA8PSA1MDApICU+JQogIGdncGxvdChhZXMoeCA9IG9yZGVyLCB5ID0gc3ByZWFkKSkgKyAKICB5bGltKGxpbWl0cyA9IGMoMi41LCA3LjUpKSArIAogIGdlb21fbGluZShzaXplID0gMSkgKyAKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSB0aHJlc2hvbGQsIGNvbG9yID0gInJlZCIsIHNpemUgPSAxLCBsaW5ldHlwZSA9IDIpICsgCiAgYW5ub3RhdGUoInRleHQiLCB4ID0gNDAwLCB5ID0gdGhyZXNob2xkICsgMC4yLCBsYWJlbCA9ICJMYXJnZXN0IHJhbmRvbSBvdXRjb21lIikKCiMgaGVhZChTcHJlYWRzLCAxMCkKCmBgYAoKPCEtLSBEYXkzIC0tPgoKIyMgUmFuZG9tbmVzcyBpcyB0aGUga2V5IHRvIHNpbXVsYXRpb24KCi0gcmFuZG9tIG51bWJlciBnZW5lcmF0aW9uIGlzIHRoZSB3b3JraG9yc2Ugb2Ygc2ltdWxhdGlvbgotIExldCdzIFRSWSBJVCEKICAgIDEuIHdyaXRlIGRvd24gYSBzZXJpZXMgb2YgdHdlbnR5IDAncyBhbmQgMSdzIGluIHJhbmRvbSBvcmRlciAKICAgICAgICAtIGFzIHJhbmRvbSBhcyB5b3UgY2FuCiAgICAgICAgLSBqdXN0IHVzZSB5b3VyIGJyYWluLCBub3RoaW5nIGVsc2UKICAgIDIuIGFmdGVyIHlvdSBmaW5pc2gsIGxldCBSIHRyeTogYHJlc2FtcGxlKDA6MSwgMjApYAogICAgMy4gQ2FyZWZ1bGx5IGluc3BlY3QgdGhlIHJlc3VsdHMgeW91IGFuZCB5b3VyIG5laWdoYm9yIGNyZWF0ZWQuLi4gaXMgdGhlcmUgYW55dGhpbmcgc3VycHJpc2luZz8KLSBROiBXaGF0IGFyZSBzb21lIG90aGVyIHdheXMgdG8gZ2VuZXJhdGUgdHJ1bHkgcmFuZG9tIG91dGNvbWVzIHdpdGhvdXQgYSBjb21wdXRlcgogICAgLSBCaW5hcnkgb3V0Y29tZSAoVC9GKQogICAgLSBSYW5kb20gYXNzaWdubWVudCBpbnRvIHRocmVlIGdyb3VwcyAoZXF1YWwgaW4gc2l6ZSkKICAgIC0gUmFuZG9tIGFzc2lnbm1lbnQgaW50byB0aHJlZSBncm91cHMgKE5PVCBlcXVhbCBpbiBzaXplKQogICAgLSBSYW5kb20gMTAtZGlnaXQgbnVtYmVyCiAgICAtIFNhbXBsZSB3aXRoIHJlcGxhY2VtZW50IGZyb20gYSBzZXQgb2YgNDMgbWVhc3VyZW1lbnRzIChlLmcuLCBlYWNoIGlzIHRoZSB3ZWlnaHQgb2YgYW4gb2JqZWN0KQoKIVtpbWFnZSBjcmVkaXQ6IGh0dHBzOi8vbS54a2NkLmNvbS8xMjEwL10ocmFuZG9tX3hrY2QxMjEwLnBuZykKCgojIyBSYW5kb20gbnVtYmVyIGdlbmVyYXRvcnMKCi0gcmFuZG9tIGlzbid0IGhhcGhhemFyZC4uLiByYW5kb20gaGFzICJzdHJ1Y3R1cmUiCi0gcmFuZG9tIG51bWJlciBnZW5lcmF0b3JzIGFyZSB0aGUgd29ya2hvcnNlIG9mIHNpbXVsYXRpb24KICAgIC0gaGFyZHdhcmUgKHRydWUpIHJhbmRvbSBudW1iZXIgZ2VuZXJhdG9ycwogICAgLSBwc2V1ZG8gcmFuZG9tIG51bWJlciBnZW5lcmF0b3JzIAotIHN1cnByaXNpbmdseSBjaGFsbGVuZ2luZyB0byBtYW51ZmFjdHVyZSByYW5kb21uZXNzIQogICAgLSAxOTk2OiBEaWVoYXJkIHRlc3RzCiAgICAgICAgLSBiYXR0ZXJ5IG9mIDE1IHRlc3RzCiAgICAgICAgLSA8aHR0cHM6Ly9lbi53aWtpcGVkaWEub3JnL3dpa2kvRGllaGFyZF90ZXN0cz4KICAgIC0gMjAwNzogVGVzdFUwMSAKICAgICAgICAtIFNtYWxsIENydXNoICgxMCB0ZXN0cykKICAgICAgICAtIENydXNoICg5NiB0ZXN0cykKICAgICAgICAtIEJpZyBjcnVzaCAoMTYwIHRlc3RzKQoKCiMjIFBvcHVsYXIgcmFuZG9taXppbmcgZnVuY3Rpb25zIGF2YWlsYWJsZSBpbiBSCgotICoqYHJ1bmlmKCApYCoqCi0gYHNhbXBsZSggKWAKLSBgc2h1ZmZsZSggKWAKLSBgcmVzYW1wbGUoIClgCi0gbmFtZS1icmFuZCBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zCiAgICAtIGBybm9ybSggKWAKICAgIC0gYHJiaW5vbSggKWAKICAgIC0gZXRjLi4uCgpgYGB7cn0Kc2FtcGxlKGMoInJlZCIsICJibHVlIiwgVFJVRSwgMi43MSksIHNpemUgPSAxKQpzYW1wbGUoMToxMiwgc2l6ZSA9IDEpCnNhbXBsZShsZXR0ZXJzLCBzaXplID0gMSkKc2FtcGxlKExFVFRFUlMsIHNpemUgPSAxKQoKYGBgCgoKIyMgbm90ZSBhYm91dCB1bmlmb3JtWzAsIDFdIAoKLSBnZW5lcmF0aW5nIHVuaWZvcm0gcmFuZG9tIG51bWJlcnMgYXJlIHN1cGVyIGltcG9ydGFudCAKLSBhbnkgcmFuZG9tIHNjaGVtZSB5b3Ugd2FudCB0byBjcmVhdGUgY2FuIGFsbW9zdCBjZXJ0YWlubHkgYnVpbHQgdXAgZnJvbSByYW5kb20gdW5pZm9ybQogICAgLSBROiBjb2luIGZsaXBzPwogICAgLSBROiByYW5kb21seSBzZWxlY3QgMjAlIG9mIHNvbWUgc2V0PwogICAgLSBROiBzYW1wbGUgZnJvbSBhIE5vcm1hbCBkaXN0cmlidXRpb24/CgoKCiMjIEJydXNoIHdpdGggZGVzdGlueQoKLSBFdmVyIHdvbmRlciBob3cgY2hhbmNlIGVuY291bnRlcnMgbWlnaHQgc2hhcGUgdGhlIHJlc3Qgb2YgeW91ciBsaWZlPwotIFRoZXJlIGlzIHNvbWVvbmUgYXQgUGVubiBTdGF0ZSB3aG8gaXMgdGhlIHZlcnkgY2xvc2VzdCBwZXJzb25hbCBtYXRjaCBmb3IgeW91IGFtb25nIHRob3NlIHlvdSd2ZSBuZXZlciBtZXQKICAgIC0gc291bG1hdGUKICAgIC0gZ3JlYXRlc3QgZnJpZW5kIHlvdSBjb3VsZCBldmVyIGtub3cKICAgIC0gcG90ZW50aWFsIHNwb3VzZQogICAgLSBpZGVhbCBidXNpbmVzcyBwYXJ0bmVyIAogICAgLSB3aGF0ZXZlciBmbG9hdHMgeW91ciBib2F0IChhbmQgdGhlaXJzLCBhcHBhcmVudGx5ISkKLSBZb3UgZG9uJ3Qga25vdyB0aGlzIHBlcnNvbiwgYnV0IHlvdSBib3RoIGFsd2F5cyBidXkgY29mZmVlIGF0IHRoZSBIVUIgQm9va3N0b3JlIGJldHdlZW4gY2xhc3NlcyBvbiBNV0YKICAgIC0gWW91IGJvdGggaGF2ZSBhIGNsYXNzIGJyZWFrIGZyb20gYWJvdXQgMTAgdG8gMTFhbQogICAgLSBZb3UgYm90aCBzd2luZyBieSBTdGFyYnVja3Mgc29tZXRpbWUgZHVyaW5nIHRoYXQgYnJlYWsKLSBHb2FsOiBXaGF0J3MgdGhlIGNoYW5jZSB0aGF0IHRoZSB0d28gb2YgeW91IG1lZXQ/CiAgICAtIFE6IEhvdyBjYW4geW91IHNpbXVsYXRlIHRoaXMgc2NlbmFyaW8/ICAKCgoKIyMgU2ltdWxhdGlvbgoKYGBge3J9Cm4gPC0gMTAwMDAKc2ltX21lZXQgPC0gZGF0YS5mcmFtZSgKICB5b3UgPC0gcnVuaWYobiwgbWluID0gMCwgbWF4ID0gNjApLCAKICBkZXN0aW55IDwtIHJ1bmlmKG4sIG1pbiA9IDAsIG1heCA9IDYwKSkgJT4lCiAgbXV0YXRlKHJlc3VsdCA9IGlmZWxzZShhYnMoeW91IC0gZGVzdGlueSkgPD0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgIlNhbWUgdGltZSwgc2FtZSBwbGFjZSEiLCAiU28gY2xvc2UsIGFuZCB5ZXQgc28gZmFyLi4uIikpCgp0YWxseSh+IHJlc3VsdCwgZm9ybWF0ID0gInBlcmNlbnQiLCBkYXRhID0gc2ltX21lZXQpCgpkZXN0aW55TW9kZWwgPC0gYmlub20udGVzdCh+IHJlc3VsdCwgbiwgc3VjY2VzcyA9ICJTYW1lIHRpbWUsIHNhbWUgcGxhY2UhIiwgZGF0YSA9IHNpbV9tZWV0KQpjb25maW50KGRlc3RpbnlNb2RlbCkKYGBgCgoKYGBge3J9CnNpbV9tZWV0ICU+JQogIGdncGxvdCgpICsgCiAgZ2VvbV9wb2ludChhZXMoeCA9IGRlc3RpbnksIHkgPSB5b3UsIGNvbG9yID0gcmVzdWx0KSwgYWxwaGEgPSAwLjMpICsgCiAgeWxhYigiWW91IGFycml2ZSAobWludXRlcyBhZnRlciAxMGFtKSIpICsgCiAgeGxhYigiRGVzdGlueSBhcnJpdmVzIChtaW51dGVzIGFmdGVyIDEwYW0pIikgKyAKICBnZ3RpdGxlKCIoc2ltdWxhdGVkKSBQcm9iYWJpbGl0eSB5b3UnbGwgbWVldCB5b3VyIGRlc3RpbnkgYXQgU3RhcmJ1Y2tzIikKICAKYGBgCgo8IS0tIERheTQgLS0+CgojIyBLZXkgcHJpbmNpcGxlcyBpbiBzaW11bGF0aW9uCgotIGRlc2lnbgotIG1vZHVsYXJpdHkKLSByZXBsaWNhdGlvbgoKCgojIyBLZXkgcHJpbmNpcGxlczogRGVzaWduIAoKLSBzdGFydCBzaW1wbGUgCi0gYWRkIGNvbXBsZXhpdHkgZ3JhZHVhbGx5IGFzIHlvdSBidWlsZCB1cCB0b3dhcmQgYSBtb3JlIHJlYWxpc3RpYyBzaW11bGF0aW9uCgojIyMjIEJydXNoIHdpdGggZGVzdGlueSBkZXNpZ24KCi0gU3RhcmJ1Y2tzIGFycml2YWwgdGltZSBpcyAqKnVuaWZvcm0qKiBiZXR3ZWVuIDEwYW0gJiAxMWFtICg2MCBtaW51dGUgd2luZG93KSBmb3IgYm90aAotIGlmIHlvdSdyZSBib3RoIHRoZXJlIGF0IHRoZSBzYW1lIHRpbWUsIHlvdSdsbCBkZWZpbml0ZWx5IG1lZXQKLSBROiBob3cgbWlnaHQgeW91IGNoYWxsZW5nZSB0aGVzZSBhc3N1bXB0aW9ucz8KCgojIyBLZXkgcHJpbmNpcGxlczogTW9kdWxhcml0eQoKLSBvZnRlbiBoZWxwZnVsIHRvIHdyYXAgdXAgdGhlIHNpbXVsYXRpb24gYXMgYSBmdW5jdGlvbgogICAgLSBjYW4gdGhlbiBjYWxsIGl0IHJlcGVhdGVkbHkgKHdpdGhvdXQgY29weS9wYXN0ZSkKICAgIC0gbW9kaWZ5IG9wdGlvbnMgYW5kIHBhcmFtZXRlcnMgYXMgYXJndW1lbnRzIHRvIHlvdXIgZnVuY3Rpb24KLSBpdCBwYXlzIHRvIHBsYW4gd2hhdCBmZWF0dXJlcyB5b3VyIHNpbXVsYXRpb24gd2lsbCBuZWVkICYgY29uc2lkZXIgc3BsaXR0aW5nIHRoZW0gb2ZmIGFzIGRpZmZlcmVudCBmdW5jdGlvbnMKICAgIC0gZWFzeSB0byByZXVzZSB0aG9zZSBmdW5jdGlvbnMgaW4gb3RoZXIgc2ltdWxhdGlvbnMKICAgIC0gb2Z0ZW4gZWFzaWVyIHRvIHJlYWQgdGhlIGNvZGUKCmBgYHtyfQpzdGFyYnVja3Nfc2ltIDwtIGZ1bmN0aW9uKG51bV9zaW0gPSAxMDAwLCB3YWl0ID0gMTApIHsgCiAgIyBwdXJwb3NlOiBzaW11bGF0ZSBjaGFuY2UgbWVldGluZyBiZXR3ZWVuIHR3byBwZW9wbGUgCiAgIyMjIG51bV9zaW06IG51bWJlciBvZiB0aW1lcyB0byByZXBsaWNhdGUgc2ltdWxhdGlvbgogICMjIyB3YWl0OiBudW1iZXIgb2YgbWludXRlcyBiZXR3ZWVuIGFycml2YWxzIGZvciBzdWNjZXNzZnVsIG1lZXRpbmcKc2ltX21lZXQgPC0gZGF0YS5mcmFtZSgKICB5b3UgPC0gcnVuaWYobiwgbWluID0gMCwgbWF4ID0gNjApLCAKICBkZXN0aW55IDwtIHJ1bmlmKG4sIG1pbiA9IDAsIG1heCA9IDYwKSkgJT4lCiAgbXV0YXRlKHJlc3VsdCA9IGlmZWxzZShhYnMoeW91IC0gZGVzdGlueSkgPD0gMTAsIAogICAgICAgICAgICAgICAgICAgICAgICAgIlNhbWUgdGltZSwgc2FtZSBwbGFjZSEiLCAiU28gY2xvc2UsIGFuZCB5ZXQgc28gZmFyLi4uIikpCgpkZXN0aW55TW9kZWwgPC0gYmlub20udGVzdCh+IHJlc3VsdCwgbiwgc3VjY2VzcyA9ICJTYW1lIHRpbWUsIHNhbWUgcGxhY2UhIiwgZGF0YSA9IHNpbV9tZWV0KQpyZXR1cm4oY29uZmludChkZXN0aW55TW9kZWwpKQp9CgpzdGFyYnVja3Nfc2ltKCkKCmBgYAoKCiMjIEtleSBwcmluY2lwbGVzOiBSZXByb2R1Y2liaWxpdHkKCmBgYHtyfQpzdGFyYnVja3Nfc2ltKCkKYGBgCgotIHVoIG9oLi4uIHRoYXQncyBub3QgdGhlIHNhbWUgYW5zd2VyIHdlIGhhZCBsYXN0IHRpbWUhCi0gcmVwcm9kdWNpYmlsaXR5IG9mIHNpbXVsYXRpb25zCiAgICAtIFIgZnVuY3Rpb25zIGxpa2UgYHJ1bmlmKCApYCBhcmUgY2FsbGVkICpwc3VlZG8qIHJhbmRvbSBiZWNhdXNlIGl0J3MgZGV0ZXJtaW5pc3RpYyBpZiB0aGUgaW5pdGlhbCBzdGF0ZSBpcyBrbm93bgogICAgLSB0aGUgaW5pdGlhbCBzdGF0ZSBpcyBjYWxsZWQgdGhlICJzZWVkIgogICAgLSBgc2V0LnNlZWQoIClgIGxldHMgdGhlIHVzZXIgZGVmaW5lIHRoZSBpbml0aWFsIHN0YXRlIGluIFIKICAgIC0gb3RoZXJ3aXNlLCBSIHVzZXMgdGhlIHN5c3RlbSBjbG9jayB0byBjaG9vc2UgYSBzZWVkIGVhY2ggdGltZSB5b3UgcnVuIHRoZSBjb2RlCgpgYGB7cn0KIyBjaG9vc2Ugc2VlZApzZXQuc2VlZCgyMykgICMgc2V0IGluaXRpYWwgc3RhdGUgb2YgUk5HIHNlZWQKcnVuaWYobiA9IDUpICAjIG5ldyBvdXRjb21lCnJ1bmlmKG4gPSA1KSAgIyBuZXcgb3V0Y29tZQoKIyB0aGlzIGlzIHdoeSB3ZSBzYXkgKnBzdWVkbyogcmFuZG9tCnNldC5zZWVkKDIzKSAgIyBzZXQgaW5pdGlhbCBzdGF0ZSBvZiBSTkcgc2VlZApydW5pZihuID0gNSkgICMgYmFjayB0byBvdXRjb21lIDEgYWdhaW4KCiMgZGlmZmVyZW50IHNlZWQKc2V0LnNlZWQoMzAxKSAjIG5ldyBzZWVkCnJ1bmlmKG4gPSA1KSAgIyBuZXcgb3V0Y29tZQoKYGBgCgoKIyMgS2V5IHByaW5jaXBsZXM6IFJlcGxpY2F0aW9uCgotIGhvdyBtYW55IHRpbWVzIGRvIHdlIHdhbnQgdG8gcmVwbGljYXRlIHRoaXMgc2ltdWxhdGlvbj8gCi0gcmVzdWx0cyB3aWxsIGJlIHNlbnNpdGl2ZSB0byB0aGUgbnVtYmVyIG9mIGNvbXB1dGF0aW9ucyB0aGF0IHdlJ3JlIHdpbGxpbmcgdG8gZXhlY3V0ZQotIFNvLCBob3cgbWFueT8gIEl0J3MgYSBiYWxhbmNlOgogICAgLSBtb3JlIHNpbXVsYXRlZCByZXBsaWNhdGlvbnMgbGVhZHMgdG8gZ3JlYXRlciBwcmVjaXNpb24gb2Ygb3VyIGVzdGltYXRlcwogICAgLSB1bm5lY2Vzc2FyeSBjYWxjdWxhdGlvbnMgd2FzdGUgdGltZSBhbmQgY29tcHV0aW5nIHJlc291cmNlcwotIFE6IGNvbXBhcmUgdGhlIHJlc3VsdHMgc2hvd24KICAgIC0gd2hhdCBpcyBgbnVtX3NpbXNgIHZzIGBuYD8KICAgIC0gd2hhdCBzaG91bGQgYG1lYW5gICYgYHNkYCBtZWFuIHRvIHVzPwoKCmBgYHtyfQpzaW1wbGVfc2ltIDwtIGZ1bmN0aW9uKG51bV9zaW0gPSAxMDAwLCB3YWl0ID0gMTApIHsgCiAgIyBwdXJwb3NlOiBsZWFuIHZlcnNpb24gb2YgYHN0YXJidWNrc19zaW1gIHRvIGV4cGxvcmUgcmVwbGljYXRpb24gIAogIHlvdSA8LSBydW5pZihudW1fc2ltLCBtaW4gPSAwLCBtYXggPSA2MCkKICBkZXN0aW55IDwtIHJ1bmlmKG51bV9zaW0sIG1pbiA9IDAsIG1heCA9IDYwKQogIHJldHVybihzdW0oYWJzKHlvdSAtIGRlc3RpbnkpIDw9IHdhaXQpIC8gbnVtX3NpbSkKfQoKcmVwcyA8LSAyNTAwCnBhcmFtcyA8LSBkYXRhLmZyYW1lKG51bV9zaW1zID0gYygxMDAsIDQwMCwgMTYwMCkpCnNpbV9yZXN1bHRzIDwtIAogIHBhcmFtcyAlPiUKICBncm91cF9ieShudW1fc2ltcykgJT4lCiAgZHBseXI6OmRvKG1vc2FpYzo6ZG8ocmVwcykgKiBzaW1wbGVfc2ltKG51bV9zaW0gPSAuJG51bV9zaW1zLCB3YWl0ID0gMTApKQpmYXZzdGF0cyhzaW1wbGVfc2ltIH4gbnVtX3NpbXMsIGRhdGEgPSBzaW1fcmVzdWx0cykKYGBgCgpgYGB7cn0Kc2ltX3Jlc3VsdHMgJT4lCiAgZ2dwbG90KGFlcyh4ID0gc2ltcGxlX3NpbSwgY29sb3IgPSBmYWN0b3IobnVtX3NpbXMpKSkgKyAKICBnZW9tX2RlbnNpdHkoc2l6ZSA9IDIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKCJQcm9wb3J0aW9uIG9mIHRpbWVzIHlvdSBtZWV0IikKYGBgCgoKCgojIyBNb2RpZnlpbmcgdGhlIHNpbXVsYXRpb24gb2Ygb3VyIEJydXNoIHdpdGggRGVzdGlueQoKLSBROiB3aGF0J3MgdGhlIGF2ZXJhZ2UgdGltZSBpdCB0YWtlcyB0byBhcnJpdmUgYXQgU3RhcmJ1Y2tzIGFmdGVyIGNsYXNzPwotIFE6IHdoYXQncyB0aGUgYXZlcmFnZSB0aW1lIGl0IHRha2VzIHRoZSBvdGhlciBwZXJzb24/Pz8KLSBROiBCeSB0aGUgd2F5LCB3aGF0J3MgdGhlIHByb2JhYmlsaXR5IHlvdSBhY3R1YWxseSB0YWxrIHRvIHRoaXMgcGVyc29uIGlmIHRoZXkgYXJlIGV2ZW4gdGhlcmU/PwoKIyMgTW9kaWZ5aW5nIHRoZSBzaW11bGF0aW9uIG9mIG91ciBCcnVzaCB3aXRoIERlc3RpbnkKCiMjIyMgQXNzdW1wdGlvbnMgbW9kaWZpZWQKCjEuIHlvdXIgYXJyaXZhbCB0aW1lIGlzIDUgbWludXRlcyArIHJhbmRvbSBkZWxheXMgCiAgICAtIHlvdXIgbWluaW11bSBwb3NzaWJsZSB0aW1lIGlzIDUgbWludXRlcwogICAgLSByYW5kb20gZGVsYXlzOiBgcmV4cChuLCAuMSlgIAogICAgICAgIC0gdHlwaWNhbGx5IDEwIG1pbnV0ZXMgb3IgbGVzcwogICAgICAgIC0gcG9zc2libHkgbG9uZ2VyIAogICAgICAgIC0gbmV2ZXIgbmVnYXRpdmUKMi4gb3RoZXIgcGVyc29uIGFycml2YWwgdGltZSBpcyB1bmtub3duICsgcmFuZG9tIGRlbGF5cwogICAgLSBmYXN0ZXN0IGFycml2YWwgaXMgdW5rbm93biwgd2UgYXNzdW1lIHVuaWZvcm0gYmV0d2VlbiAzICYgMjAgbWludXRlcwogICAgLSByYW5kb20gZGVsYXlzOiBgcmV4cChuLCAuMSlgIAozLiBtYXliZSA1JSBjaGFuY2UgeW91J3JlIGJvdGggd2lsbGluZyB0byBzdHJpa2UgdXAgYSBtZWFuaW5nZnVsIGNvbnZlcnNhdGlvbiB3aXRoIGEgc3RyYW5nZXIgYXQgU3RhcmJ1Y2tzIG9uIGFueSBnaXZlbiBkYXkKNC4gUTogU2hhcmUgc29tZSBhZGRpdGlvbmFsIHRoaW5ncyB3ZSBzdGlsbCAqKmhhdmVuJ3QqKiBjb25zaWRlcmVkIGhlcmUhCi0gTm90ZSBhYm91dCBtb2RlbGluZyB3YWl0aW5nIHRpbWUgKGkuZS4sIGRlbGF5KQogICAgLSBMb3RzIG9mICJuYW1lLWJyYW5kIiBwcm9iYWJpbGl0eSBkaXN0cmlidXRpb25zIG1vZGVsIHdhaXRpbmcgdGltZSBzY2VuYXJpb3MKICAgIC0gZG9uJ3QgZ2V0IHN0dWNrIG9uIHRoZSBkZXRhaWxzIG9mIHRoYXQgY2hvaWNlIGhlcmUsIGp1c3QgcGF5IGF0dGVudGlvbiBpbiBTVEFUIDQxNCEKICAgIC0gb3VyIGNob2ljZTogYGdncGxvdCgpICsgZ2VvbV9kZW5zaXR5KGFlcyh4ID0gcmV4cChuLCAuMSkpKWAKCgpgYGB7cn0KbiA8LSAxMDAwMDAKc2ltX21lZXRFeHAgPC0gZGF0YS5mcmFtZSgKICB5b3UgPC0gNSArIHJleHAobiwgMC4xKSwgICAgICAgICAgICAgICAgICAgICAgIyB5b3VyIGFycml2YWwgdGltZSAoNSBtaW4gKyByYW5kb20gZGVsYXkpCiAgZGVzdGlueSA8LSBydW5pZihuLCBtaW4gPSAzLCBtYXggPSAyMCkgKyAgICAgICMgZmFzdGVzdCBhcnJpdmFsIGlzIHVua25vd24gYmV0d2VlbiAzICYgMjAgbWluCiAgICAgICAgICAgICByZXhwKG4sIDAuMSkpICU+JSAgICAgICAgICAgICAgICAgICMgcmFuZG9tIGRlbGF5CiAgbXV0YXRlKG9wcG9ydHVuaXR5ID0gaWZlbHNlKGFicyh5b3UgLSBkZXN0aW55KSA8PSAxMCwgCiAgICAgICAgICAgICAgICAgICAgICAgICAiU2FtZSB0aW1lLCBzYW1lIHBsYWNlISIsICJTbyBjbG9zZSwgYW5kIHlldCBzbyBmYXIuLi4iKSwgCiAgICAgICAgIHRhbGthdGl2ZU1vb2QgPSBzYW1wbGUoYyhUUlVFLCBGQUxTRSksIHNpemUgPSBuLCAgICAKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXBsYWNlID0gVFJVRSwgcHJvYiA9IGMoMC4wNSwgMC45NSkpLAogICAgICAgICByZXN1bHQgPSBpZmVsc2UodGVzdCA9IChvcHBvcnR1bml0eSA9PSAiU2FtZSB0aW1lLCBzYW1lIHBsYWNlISIgJiB0YWxrYXRpdmVNb29kID09IFRSVUUpLCAKICAgICAgICAgICAgICAgICAgICAgICAgIHllcyA9ICJCbGlzcyIsIG5vID0gIk9oIHdlbGwuLi4iKSkKCnRhbGx5KH4gcmVzdWx0LCBmb3JtYXQgPSAicGVyY2VudCIsIGRhdGEgPSBzaW1fbWVldEV4cCkKCmRlc3RpbnlNb2RlbCA8LSBiaW5vbS50ZXN0KH4gcmVzdWx0LCBuLCBzdWNjZXNzID0gIkJsaXNzIiwgZGF0YSA9IHNpbV9tZWV0RXhwKQpjb25maW50KGRlc3RpbnlNb2RlbCkKYGBgCgpgYGB7cn0Kc2ltX21lZXRFeHAgJT4lCiAgZ2dwbG90KCkgKyAKICBnZW9tX3BvaW50KGFlcyh4ID0gZGVzdGlueSwgeSA9IHlvdSwgY29sb3IgPSByZXN1bHQpLCBhbHBoYSA9IDAuMykgKyAKICB5bGFiKCJZb3UgYXJyaXZlIChtaW51dGVzIGFmdGVyIDEwYW0pIikgKyAKICB4bGFiKCJEZXN0aW55IGFycml2ZXMgKG1pbnV0ZXMgYWZ0ZXIgMTBhbSkiKSArIAogIGdndGl0bGUoIlRoaXMgaXMgd2h5IGl0J3Mgc28gaGFyZCB0byBtZWV0IHBlb3BsZS4uLiIpICsgCiAgc2NhbGVfeF9jb250aW51b3VzKGxpbWl0cyA9IGMoMCwgNjApKSArIAogIHNjYWxlX3lfY29udGludW91cyhsaW1pdHMgPSBjKDAsIDYwKSkgCiAgCmBgYAoKPCEtLSBEYXk1OyBtb3ZlZCBhZHZpc2luZyBzaW0gdG8gRGF5NiAtLT4KCgojIyBTaW11bGF0aW5nIE1vZGVscwoKLSBJdCdzIG9mdGVuIGhhbmR5IHRvIGRpcmVjdGx5IHNpbXVsYXRlIGEgc3RhdGlzdGljYWwgbW9kZWwgb2Ygc29tZSBraW5kIGluIG9yZGVyIHRvIGJ1aWxkIGludHVpdGlvbiBhYm91dCBpdCdzIHByb3BlcnRpZXMKICAgIC0gU2ltdWxhdGUgZGF0YSB0aGF0IGNvdWxkIGhhdmUgYmVlbiBwcm9kdWNlZCBieSBzb21lIG1vZGVsCiAgICAtIFNpbXVsYXRlIHNlbnNpdGl2aXR5IHRvIG1vZGVsIGFzc3VtcHRpb25zCgoKCiMjIFNpbXVsYXRpbmcgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIGRhdGEKCkNvbnNpZGVyIGEgc2ltcGxlIGxpbmVhciByZWdyZXNzaW9uIG1vZGVsOgoKXFt5ID0gXGJldGFfMCArIFxiZXRhXzEqeFxdCgotIHN1Y2ggdGhhdAogICAgLSBZIGlzIHNvbWUgbWVhc3VyYWJsZSByZXNwb25zZSB2YXJpYWJsZSB0aGF0IHdlIHdhbnQgdG8gcHJlZGljdAogICAgLSBYIGlzIHNvbWUgbWVhc3VyYWJsZSBleHBsYW5hdG9yeSB2YXJpYWJsZSB1c2VkIHRvIHByZWRpY3QgWQotIGFnYWluLCB3ZSBuZWVkIHRvIGludGVncmF0ZSBzb21lIGRlc2lyZWQgbW9kZWwgd2l0aCBzb21lICJub2lzZSIgZHVlIHRvIHJhbmRvbW5lc3MKICAgIC0gV2hhdCB3aWxsIGJlICoqZml4ZWQqKj8KICAgIC0gV2hhdCB3aWxsIGJlICoqcmFuZG9tKio/CgoKIyMgU2ltdWxhdGluZyBzaW1wbGUgbGluZWFyIHJlZ3Jlc3Npb24gZGF0YQoKQ29uc2lkZXIgYSBsb2dpc3RpYyByZWdyZXNzaW9uIG1vZGVsOgoKXFtFW1kgfCBYXSA9IFxiZXRhXzAgKyBcYmV0YV8xKlhcXQoKXFt5X2kgPSBiXzAgKyBiXzEqeF9pICsgXGVwc2lsb25faVxdCgotIFE6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHJlcHJlc2VudGF0aW9ucz8KLSBROiBDYW4geW91IHNwb3Qgb3VyIGlucHV0cyBpbiB0aGUgbW9kZWwgc3VtbWFyeT8KLSBROiBXaGF0IGlmIHdlIGNoYW5nZSB0aGUgc2FtcGxlIHNpemU/CgoKYGBge3J9CiMgYmV0YV8wIAppbnRlcmNlcHQgPC0gMjYKCiMgYmV0YV8xCmJldGEgPC0gLTIuNQoKIyBzYW1wbGUgc2l6ZSBmb3Igb3VyIHNpbXVsYXRlZCBtb2RlbApuIDwtIDUwMDAKCiMgWCBpcyBhIHNpbXVsYXRlZCByYW5kb20gdmFyaWFibGUgcmVwcmVzZW50aW5nIHNvbWUgbWVhc3VyYWJsZSBjaGFyYWN0ZXJpc3RpYyBvZiB0aGUgY2FzZXMgCnh0ZXN0IDwtIHJub3JtKG4sIG1lYW4gPSAxMCwgc2QgPSAxKQoKIyBzaW11bGF0ZSBlcnJvcnM6IGFzc3VtZSBpLmkuZC4gTigwLCBzaWdtYSkKZXJyb3JzIDwtIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IDUpCgojIFkgaXMgYSBzaW11bGF0ZWQgcmFuZG9tIHZhcmlhYmxlIHJlcHJlc2VudGluZyBvdXIgcmVzcG9uc2UKeXRlc3QgPC0gaW50ZXJjZXB0ICsgKHh0ZXN0ICogYmV0YSkgKyBlcnJvcnMKCiMgZml0IHRoZSBzaW11bGF0ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApzaW1yZWcgPC0gbG0oeXRlc3QgfiB4dGVzdCkKCiMgaG93IGRpZCB3ZSBkbz8KbXN1bW1hcnkoc2ltcmVnKQojIGNvbmZpbnQoc2ltcmVnKQoKYGBgCgoKCgoKIyMgU2ltdWxhdGluZyBsb2dpc3RpYyByZWdyZXNzaW9uIGRhdGEKCkNvbnNpZGVyIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbDoKClxbbG9nKFxmcmFje1xwaX17MS1ccGl9KSA9IFxiZXRhXzAgKyBcYmV0YV8xKnhcXQoKLSBzdWNoIHRoYXQsIAogICAgLSAkXHBpJCByZXByZXNlbnRzIHRoZSBwb3B1bGF0aW9uIHByb2JhYmlsaXR5IG9mICJzdWNjZXNzIiBhY2NvcmRpbmcgdG8gc29tZSBiaW5hcnkgb3V0Y29tZSBvZiBpbnRlcmVzdCB0byB1cwogICAgLSBYIGlzIHNvbWUgbWVhc3VyYWJsZSBleHBsYW5hdG9yeSB2YXJpYWJsZSB1c2VkIHRvIHByZWRpY3QgdGhlIG9kZHMgb2YgInN1Y2Nlc3MiCgotIGFnYWluLCB3ZSBuZWVkIHRvIGludGVncmF0ZSBzb21lIGRlc2lyZWQgbW9kZWwgd2l0aCBzb21lICJub2lzZSIgZHVlIHRvIHJhbmRvbW5lc3MKICAgIC0gV2hhdCB3aWxsIGJlICoqZml4ZWQqKj8KICAgIC0gV2hhdCB3aWxsIGJlICoqcmFuZG9tKio/CgoKIyMgU2ltdWxhdGluZyBsb2dpc3RpYyByZWdyZXNzaW9uIGRhdGEKCkNvbnNpZGVyIGEgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbDoKClxbbG9nKFxmcmFje1xwaX17MS1ccGl9KSA9IFxiZXRhXzAgKyBcYmV0YV8xKnhcXQoKXFtsb2coXGZyYWN7cF9pfXsxLXBfaX0pID0gYl8wICsgYl8xKnhfaVxdCgotIFE6IFdoYXQncyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZXNlIHJlcHJlc2VudGF0aW9ucz8KLSBROiBXaHkgdGhpcyBzdGVwPyBgaWZlbHNlKHJ1bmlmKG4pIDwgcHJvYiwgMSwgMClgCgoKYGBge3J9CiMgYmV0YV8wCmludGVyY2VwdCA8LSAtMQoKIyBiZXRhXzEKYmV0YSA8LSAwLjUKCiMgc2FtcGxlIHNpemUgZm9yIG91ciBzaW11bGF0ZWQgbW9kZWwKbiA8LSA1MDAwCgojIFggaXMgYSBzaW11bGF0ZWQgcmFuZG9tIHZhcmlhYmxlIHJlcHJlc2VudGluZyBzb21lIG1lYXN1cmFibGUgY2hhcmFjdGVyaXN0aWMgb2YgdGhlIGNhc2VzIAp4dGVzdCA8LSBybm9ybShuLCBtZWFuID0gMSwgc2QgPSAxKQoKIyByZWNhbGw6IHRoZSBsb2ctb2RkcyBpcyBhICpsaW5lYXIqIG1vZGVsIHdpdGggcmVzcGVjdCB0byBvdXIgZXhwbGFuYXRvcnkvcHJlZGljdG9yIHZhcmlhYmxlcwpsaW5wcmVkIDwtIGludGVyY2VwdCArICh4dGVzdCAqIGJldGEpCgojIHRyYW5zZm9ybSBwcmVkaWN0aW9ucyB0byBwcm9iYWJpbGl0aWVzCnByb2IgPC0gZXhwKGxpbnByZWQpLygxICsgZXhwKGxpbnByZWQpKQoKIyBZIGlzIGEgc2ltdWxhdGVkIHJhbmRvbSB2YXJpYWJsZSByZXByZXNlbnRpbmcgb3VyIHJlc3BvbnNlOyB0aGUgImVycm9ycyIgYXJlIGJ1aWx0LWluIGhlcmUKeXRlc3QgPC0gaWZlbHNlKHJ1bmlmKG4pIDwgcHJvYiwgMSwgMCkKCiMgZml0IHRoZSBzaW11bGF0ZWQgbG9naXN0aWMgcmVncmVzc2lvbiBtb2RlbApsb2dyZWcgPC0gZ2xtKHl0ZXN0IH4geHRlc3QsIGZhbWlseT1iaW5vbWlhbCkKCiMgaG93IGRpZCB3ZSBkbz8KY29lZihsb2dyZWcpCmNvbmZpbnQobG9ncmVnKQoKIyMgaW50ZXJwcmV0IG9kZHMKZXhwKGNvZWYobG9ncmVnKSkKZXhwKGNvbmZpbnQobG9ncmVnKSkKYGBgCgoKCiMjIEV2YWx1YXRpbmcgTW9kZWwgQXNzdW1wdGlvbnMKCi0gQWxsIG1vZGVscyBoYXZlIGFzc3VtcHRpb25zIG9mIG9uZSBzb3J0IG9yIGFub3RoZXIKLSBXaGVuIGFzc3VtcHRpb25zIGFyZSB2aW9sYXRlZCwgdGhlIGNvbmNsdXNpb25zIG9mIHRoZSBtb2RlbCBtYXkgYmUgamVvcGFyZGl6ZWQKICAgIC0gTm90IGFsbCB2aW9sYXRpb25zIGFyZSBlcXVhbGx5IGRhbmdlcm91cy4uLgogICAgLSBROiAqKkhvdyBjYW4gd2UgdGVsbCB3aGljaCBpcyB3aGljaD8/KioKCgojIyBFdmFsdWF0aW5nIFNpbXBsZSBMaW5lYXIgUmVncmVzc2lvbiBBc3N1bXB0aW9ucwoKLSBROiBXaGF0IGFyZSB0aGUgKDQpIGFzc3VtcHRpb25zIG9mIGxpbmVhciByZWdyZXNzaW9uPwotIFE6IEhvdyBtaWdodCB3ZSB1c2Ugc2ltdWxhdGlvbiB0byAiY2hhbGxlbmdlIiBzb21lIG9mIHRoZXNlIGFzc3VtcHRpb25zIGFuZCBzZWUgaG93IHNlbnNpdGl2ZSB0aGV5IGFyZT8KCgojIyBDaGFsbGVuZ2luZyB0aGUgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbgoKXFtFW1l8WF0gPSBcYmV0YV8wICsgXGJldGFfMVhfMSArIFxiZXRhXzJYXzJcXQoKXFt5X2kgPSBiXzAgKyBiXzF4X3sxaX0gKyBiXzJ4X3syaX0gKyBcZXBzaWxvbl9pXF0KCi0gUTogSG93IG1pZ2h0IHdlICoqY2hhbGxlbmdlKiogdGhlIGVxdWFsIHZhcmlhbmNlIGFzc3VtcHRpb24/Ci0gUTogV2hhdCBhY3R1YWxseSAqKmdvZXMgd3JvbmcqKiB3aGVuIHdlIGhhdmUgYSBwcm9ibGVtIG9mIHVuZXF1YWwgdmFyaWFuY2U/CgpgYGB7cn0KIyBzYW1wbGUgc2l6ZSBmb3Igb3VyIHNpbXVsYXRpb24KbiA8LSAyNTAKCiMgc2Qgb2YgbW9kZWwgZXJyb3JzCnJtc2UgPC0gMQoKIyB0d28gZXhwbGFuYXRvcnkgdmFyaWFibGVzCngxIDwtIHJ1bmlmKG4sIG1pbiA9IDAsIG1heCA9IDE1KQoKIyBtb2RlbCBjb2VmZmljaWVudHMKYmV0YTAgPC0gLTMKYmV0YTEgPC0gMC41CgoKIyBzaW11bGF0ZSByZXNwb25zZSBkYXRhCnkgPC0gYmV0YTAgKyBiZXRhMSp4MSArIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IHJtc2UpICAgICAgICAjIGVxdWFsIHZhcmlhbmNlCiMgeSA8LSBiZXRhMCArIGJldGExKngxICsgcm5vcm0obiwgbWVhbiA9IDAsIHNkID0gcm1zZSArIHgxKSAgICMgVU5lcXVhbCB2YXJpYW5jZQoKIyBwbG90IGRhdGEgd2l0aCBzbW9vdGhlcgpkYXRhLmZyYW1lKHksIHgxKSAlPiUKICBnZ3Bsb3QoYWVzKHggPSB4MSwgeSA9IHkpKSArIAogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV9zbW9vdGgoKQoKIyByZWdyZXNzaW9uIG1vZGVsCnNpbUxNIDwtIGxtKHkgfiB4MSkKCgojIGNoZWNrIHRoZSBtb2RlbCBmaXQKbXN1bW1hcnkoc2ltTE0pCm1wbG90KHNpbUxNKVsxXSAgIyByZXNpZHVhbCBkaWFnbm9zdGljcwoKCmBgYAoKCgoKIyMgU2ltdWxhdGUgbWFueSBtb2RlbHMKCi0gUTogV2hhdCBkbyBleHBlY3QgdGhlIGRpc3RyaWJ1dGlvbiBvZiBwYXJhbWV0ZXIgZXN0aW1hdGVzIHRvIGxvb2sgbGlrZT8KICAgIC0gSWYgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbiBpcyBzYXRpc2ZpZWQKICAgIC0gSWYgZXF1YWwgdmFyaWFuY2UgYXNzdW1wdGlvbiBpcyBOT1Qgc2F0aXNmaWVkCgoKYGBge3J9CmRvc2ltIDwtIGZ1bmN0aW9uKCkgewogIHkgPC0gYmV0YTAgKyBiZXRhMSp4MSArIHJub3JtKG4sIG1lYW4gPSAwLCBzZCA9IHJtc2UgKyB4MSkgICAjIGRlcGFydHVyZQogIG1vZCA8LSBsbSh5IH4geDEpCiAgcmVzdWx0IDwtIGNvZWZmaWNpZW50cyhtb2QpCiAgcmV0dXJuKHJlc3VsdCkKCn0KCnNpbXMgPC0gbW9zYWljOjpkbygxMDAwKSAqIGRvc2ltKCkKZmF2c3RhdHMofiB4MSwgZGF0YSA9IHNpbXMpCgojIGRpc3RyaWJ1dGlvbiBjb2VmZmljaWVudCBlc3RpbWF0ZXMKc2ltcyAlPiUKICBnZ3Bsb3QoYWVzKHggPSB4MSkpICsKICBnZW9tX2RlbnNpdHkoKSArCiAgIyBwYXNzIGFyZ3VtZW50cyB0byBgZG5vcm1gIGZ1bmN0aW9uCiAgc3RhdF9mdW5jdGlvbihmdW4gPSBkbm9ybSwgYXJncyA9IGxpc3QobWVhbiA9IG1lYW4oc2ltcyR4MSksIHNkID0gc2Qoc2ltcyR4MSkpLAogICAgICAgICAgICAgICAgbGluZXR5cGUgPSAyKSArCiAgZ2d0aXRsZSgiZGlzdHJpYnV0aW9uIG9mIHJlZ3Jlc3Npb24gcGFyYW1ldGVyIikgKwogIHNjYWxlX3hfY29udGludW91cygiYmV0YSAxIGNvZWZmaWNpZW50cyIpCgpgYGAKCgoKCiMjIFNpbXVsYXRpbmcgYSBjb21wbGV4IHN5c3RlbQoKCmBgYHtyfQphbnlfYWN0aXZlIDwtIGZ1bmN0aW9uKGRmKSB7CiAgIyByZXR1cm4gVFJVRSBpZiBzb21lb25lIGhhcyBub3QgZmluaXNoZWQKICByZXR1cm4obWF4KGRmJGVuZHRpbWUpID09IEluZikKfQoKbmV4dF9jdXN0b21lciA8LSBmdW5jdGlvbihkZikgewogICMgcmV0dXJucyB0aGUgbmV4dCBjdXN0b21lciBpbiBsaW5lCiAgcmVzIDwtIGZpbHRlcihkZiwgZW5kdGltZSA9PSBJbmYpICU+JQogICAgYXJyYW5nZShhcnJpdmFsKQogIHJldHVybihoZWFkKHJlcywgMSkpCn0KCnVwZGF0ZV9jdXN0b21lciA8LSBmdW5jdGlvbihkZiwgY3VzdF9udW0sIGVuZF90aW1lKSB7CiAgIyBzZXRzIHRoZSBlbmQgdGltZSBvZiBhIHNwZWNpZmljIGN1c3RvbWVyCiAgcmV0dXJuKG11dGF0ZShkZiwgZW5kdGltZSA9IGlmZWxzZShjdXN0bnVtID09IGN1c3RfbnVtLCBlbmRfdGltZSwgZW5kdGltZSkpKQp9CiAgCmBgYAoKCmBgYHtyfQpydW5fc2ltIDwtIGZ1bmN0aW9uKG4gPSAxLzIsIG0gPSAzLzIsIGhvdXJzID0gNikgewogICMgc2ltdWxhdGlvbiBvZiBiYW5rIHdoZXJlIHRoZXJlIGlzIGp1c3Qgb25lIHRlbGxlcgogICMgbjogZXhwZWN0ZWQgbnVtYmVyIG9mIGN1c3RvbWVycyBwZXIgbWludXRlCiAgIyBtOiBleHBlY3RlZCBsZW5ndGggb2YgdHJhbnNhY3Rpb24gaXMgbSBtaW51dGVzCiAgIyBob3VyczogYmFuayBvcGVuIGZvciB0aGlzIG1hbnkgaG91cnMKICAKICBjdXN0b21lcnMgPC0gcnBvaXMoaG91cnMgKiA2MCwgbGFtYmRhID0gbikKICBhcnJpdmFsIDwtIG51bWVyaWMoc3VtKGN1c3RvbWVycykpCiAgcG9zaXRpb24gPC0gMQogIGZvciAoaSBpbiAxOmxlbmd0aChjdXN0b21lcnMpKSB7CiAgICBudW1jdXN0IDwtIGN1c3RvbWVyc1tpXQogICAgaWYgKG51bWN1c3QgIT0gMCkgewogICAgICBhcnJpdmFsW3Bvc2l0aW9uOihwb3NpdGlvbiArIG51bWN1c3QgLSAxKV0gPC0gcmVwKGksIG51bWN1c3QpCiAgICAgIHBvc2l0aW9uIDwtIHBvc2l0aW9uICsgbnVtY3VzdAogICAgfQogIH0KICBkdXJhdGlvbiA8LSByZXhwKGxlbmd0aChhcnJpdmFsKSwgcmF0ZSA9IDEvbSkgICMgRVtYXT1tCiAgZGYgPC0gZGF0YS5mcmFtZShhcnJpdmFsLCBkdXJhdGlvbiwgY3VzdG51bSA9IDE6bGVuZ3RoKGR1cmF0aW9uKSwgCiAgICAgICAgICAgICAgICAgICBlbmR0aW1lID0gSW5mLCBzdHJpbmdzQXNGYWN0b3JzID0gRkFMU0UpCiAgCiAgZW5kdGltZSA8LSAwICMgc2V0IHVwIGJlZ2lubmluZyBvZiBzaW11bGF0aW9uCiAgd2hpbGUgKGFueV9hY3RpdmUoZGYpKSB7ICMgYW55b25lIGxlZnQgdG8gc2VydmUKICAgIG5leHRfb25lIDwtIG5leHRfY3VzdG9tZXIoZGYpCiAgICBub3cgPC0gaWZlbHNlKG5leHRfb25lJGFycml2YWwgPj0gZW5kdGltZSwgbmV4dF9vbmUkYXJyaXZhbCwgZW5kdGltZSkKICAgIGVuZHRpbWUgPC0gbm93ICsgbmV4dF9vbmUkZHVyYXRpb24KICAgIGRmIDwtIHVwZGF0ZV9jdXN0b21lcihkZiwgbmV4dF9vbmUkY3VzdG51bSwgZW5kdGltZSkKICB9CiAgZGYgPC0gbXV0YXRlKGRmLCB0b3RhbHRpbWUgPSBlbmR0aW1lIC0gYXJyaXZhbCkKICByZXR1cm4oZmF2c3RhdHMofiB0b3RhbHRpbWUsIGRhdGEgPSBkZikpCn0KCmBgYAoKCmBgYHtyfQpzaW1fcmVzdWx0cyA8LSBtb3NhaWM6OmRvKDMpICogcnVuX3NpbSgpCnNpbV9yZXN1bHRzCmBgYAoKCgo=